package com.amos.muexamclient.sys.service.impl;

import com.amos.exception.BizExceptionEnum;
import com.amos.muexamclient.framework.service.TokenService;
import com.amos.muexamclient.sys.entity.SysMenuEntity;
import com.amos.muexamclient.sys.mapper.SysMenuMapper;
import com.amos.muexamclient.sys.service.SysMenuService;
import com.amos.muexamclient.sys.service.SysRoleMenuService;
import com.amos.muexamclient.sys.service.dto.LoginUser;
import com.amos.muexamclient.sys.service.dto.SysMenuDTO;
import com.amos.muexamclient.sys.service.dto.SysUserDTO;
import com.amos.muexamclient.sys.service.dto.TreeSelect;
import com.amos.muexamcore.core.AbstractPageService;
import com.amos.muexamcore.core.BaseMapper;
import com.amos.muexamcore.util.ServletUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Copyright © 2018 五月工作室. All rights reserved.
 *
 * @Package com.amos.muexamclient.sys.service.impl
 * @ClassName SysMenuServiceImpl
 * @Description TODO
 * @Author Amos
 * @Modifier
 * @Date 2020-03-24 14:35:22
 * @Version 1.0
 **/
@Service
public class SysMenuServiceImpl extends AbstractPageService<SysMenuDTO, SysMenuEntity> implements SysMenuService {

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private SysRoleMenuService roleMenuService;

    /**
     * 条件查询
     *
     * @param dto 查询的条件
     */
    @Override
    public List<SysMenuDTO> list(SysMenuDTO dto) {
        return this.getMapper().select(dto);
    }

    /**
     * 返回BaseDao对象
     */
    @Override
    public BaseMapper<SysMenuEntity, SysMenuDTO> getMapper() {
        return sysMenuMapper;
    }

    /**
     * 保存之间的校验
     *
     * @param dto 保存的对象
     */
    @Override
    public void preValidate(SysMenuDTO dto) {
        this.validate(dto);
    }

    @Override
    public int logicDeleted(String id) {
        if (this.hasChildByMenuId(id)) {
            BizExceptionEnum.DEFINED_EXCEPTION.definedMsg("存在子菜单,不允许删除").throwException();
        }

        if (this.checkMenuExistRole(id)) {
            BizExceptionEnum.DEFINED_EXCEPTION.definedMsg("菜单已分配,不允许删除").throwException();
        }
        return super.logicDeleted(id);
    }

    private boolean checkMenuExistRole(String id) {
        int result = this.roleMenuService.checkMenuExistRole(id);
        return result > 0 ? true : false;
    }

    private boolean hasChildByMenuId(String id) {
        int result = this.sysMenuMapper.hasChildByMenuId(id);
        return result > 0 ? true : false;
    }

    private void validate(SysMenuDTO dto) {
        List<SysMenuDTO> list = this.sysMenuMapper.getByMenuNameAndParent(dto.getMenuName(), dto.getParentId());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        if (dto.isNew()) {
            BizExceptionEnum.DEFINED_EXCEPTION.definedMsg("新增菜单'" + dto.getMenuName() + "'失败，菜单名称已存在").throwException();
        } else {
            if (!list.get(0).getId().equalsIgnoreCase(dto.getId())) {
                BizExceptionEnum.DEFINED_EXCEPTION.definedMsg("修改菜单'" + dto.getMenuName() + "'失败，菜单名称已存在").throwException();
            }
        }
    }

    /**
     * 添加子集数据
     *
     * @param id  主表ID
     * @param dto 主表DTO对象
     */
    @Override
    public void appendSubsidiary(String id, SysMenuDTO dto) {

    }

    @Override
    public List<SysMenuDTO> listMenu(SysMenuDTO dto) {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUserDTO user = loginUser.getUser();
        user.setId(loginUser.getId());
        List<SysMenuDTO> menuList = null;
        // 管理员显示所有菜单信息
        if (user.isAdmin()) {
            menuList = this.sysMenuMapper.selectMenuList(dto);
        } else {
            dto.setUserId(user.getId());
            menuList = this.sysMenuMapper.selectMenuListByUserId(dto);
        }
        return menuList;
    }

    @Override
    public Set<String> selectMenuPermsByUserId(String id) {
        return sysMenuMapper.selectMenuPermsByUserId(id);
    }

    /**
     * 根据用户ID查询菜单
     *
     * @param user 用户
     * @return 菜单列表
     */
    @Override
    public List<SysMenuDTO> selectMenuTreeByUserId(SysUserDTO user) {
        List<SysMenuDTO> menus = null;
        if (user.isAdmin()) {
            menus = sysMenuMapper.selectMenuTreeAll();
        } else {
            menus = sysMenuMapper.selectMenuTreeByUserId(user.getId());
        }
        return getChildPerms(menus, "0");
    }

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list     分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<SysMenuDTO> getChildPerms(List<SysMenuDTO> list, String parentId) {
        List<SysMenuDTO> returnList = new ArrayList<SysMenuDTO>();
        for (Iterator<SysMenuDTO> iterator = list.iterator(); iterator.hasNext(); ) {
            SysMenuDTO t = (SysMenuDTO) iterator.next();
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getParentId().equalsIgnoreCase(parentId)) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     *
     * @param list
     * @param t
     */
    private void recursionFn(List<SysMenuDTO> list, SysMenuDTO t) {
        // 得到子节点列表
        List<SysMenuDTO> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysMenuDTO tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                Iterator<SysMenuDTO> it = childList.iterator();
                while (it.hasNext()) {
                    SysMenuDTO n = (SysMenuDTO) it.next();
                    recursionFn(list, n);
                }
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysMenuDTO> getChildList(List<SysMenuDTO> list, SysMenuDTO t) {
        List<SysMenuDTO> tlist = new ArrayList<SysMenuDTO>();
        Iterator<SysMenuDTO> it = list.iterator();
        while (it.hasNext()) {
            SysMenuDTO n = (SysMenuDTO) it.next();
            if (n.getParentId().equalsIgnoreCase(t.getId())) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenuDTO> list, SysMenuDTO t) {
        return getChildList(list, t).size() > 0 ? true : false;
    }


    @Override
    public List<TreeSelect> buildMenuTreeSelect(List<SysMenuDTO> menus) {
        List<SysMenuDTO> menuTrees = buildMenuTree(menus);
        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 构建前端所需要树结构
     *
     * @param menus 菜单列表
     * @return 树结构列表
     */
    @Override
    public List<SysMenuDTO> buildMenuTree(List<SysMenuDTO> menus) {
        List<SysMenuDTO> returnList = new ArrayList<SysMenuDTO>();
        for (Iterator<SysMenuDTO> iterator = menus.iterator(); iterator.hasNext(); ) {
            SysMenuDTO t = (SysMenuDTO) iterator.next();
            // 根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (SysUserDTO.isAdmin(t.getParentId())) {
                recursionFn(menus, t);
                returnList.add(t);
            }
        }
        if (returnList.isEmpty()) {
            returnList = menus;
        }
        return returnList;
    }

    @Override
    public List<TreeSelect> treeSelect() {
        SysMenuDTO dto = new SysMenuDTO();
        List<SysMenuDTO> menus = this.listMenu(dto);
        return this.buildMenuTreeSelect(menus);
    }

    @Override
    public Map<String, Object> roleMenuTreeselect(String roleId) {
        Map<String, Object> map = new HashMap<>();
        List<SysMenuDTO> menus = this.listMenu(new SysMenuDTO());
        List<String> meunsByRoleId = this.selectMenuListByRoleId(roleId);
        map.put("checkedKeys", meunsByRoleId);
        map.put("menus", this.buildMenuTreeSelect(menus));
        return map;
    }

    private List<String> selectMenuListByRoleId(String roleId) {
        return this.sysMenuMapper.selectMenuListByRoleId(roleId);
    }
}
